/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file ipwm_drv.c
 * @brief This file defines ipwm driver functions
 */

/*******Includes***************************************************************/
#include "ipwm_reg_access.h"
#include "clock_drv.h"

/*******Definitions************************************************************/

/*******Variables**************************************************************/
/*! @brief Table to save register base addresses for ipwm instances */
static IPWM_Type* s_ipwmRegBase[CONFIG_IPWM_INSTANCE_COUNT] = CONFIG_IPWM_REG_BASE_PTRS;

/*! @brief Table to save ipwm module names */
static module_names_t s_ipwmModuleName[CONFIG_IPWM_INSTANCE_COUNT] = CONFIG_IPWM_MODULE_NAMES;

#if (CONFIG_IPWM_UNIFIED_IRQ_FEATURE == 1U)
/*! @brief Table to save ipwm irq numbers */
static const IRQn_Type s_ipwmIrqId[CONFIG_IPWM_INSTANCE_COUNT] = CONFIG_IPWM_IRQS;

/*! @brief Table to save ipwm interrupt callbacks */
static ipwm_callback_t s_ipwmCallback[CONFIG_IPWM_INSTANCE_COUNT] = {NULL};
#else
/*! @brief Table to save ipwm channel irq numbers */
static const IRQn_Type s_ipwmChannelIrqId[CONFIG_IPWM_INSTANCE_COUNT] = CONFIG_IPWM_CHANNEL_IRQS;

/*! @brief Table to save ipwm overflow irq numbers */
static const IRQn_Type s_ipwmOverflowIrqId[CONFIG_IPWM_INSTANCE_COUNT] = CONFIG_IPWM_OVERFLOW_IRQS;

/*! @brief Table to save ipwm channel interrupt callbacks */
static ipwm_callback_t s_ipwmChannelCallback[CONFIG_IPWM_INSTANCE_COUNT] = {NULL};

/*! @brief Table to save ipwm overflow interrupt callbacks */
static ipwm_callback_t s_ipwmOverflowCallback[CONFIG_IPWM_INSTANCE_COUNT] = {NULL};
#endif
/*! @brief Table to save ipwm interrupt status */
static ipwm_int_state_t s_ipwmIntStatus[CONFIG_IPWM_INSTANCE_COUNT] = {0U};

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : IPWM_InitCombineOutput
 *** Description   : Configure the IPWM channel combination output related functions
 ******************************************************************************/
void IPWM_InitCombineOutput(uint32_t instance, const ipwm_combine_output_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    IPWM_Type* regBase = s_ipwmRegBase[instance];
    uint8_t i = 0U;
    ipwm_channel_t channel = IPWM_CH_0;

    /* Enable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_ipwmModuleName[instance], true);
    /* Set soft reset inactive of spwm module */
    CLOCK_SetModuleSoftResetState(s_ipwmModuleName[instance], RESET_INACTIVE);

    ASSERT_PARAM(config->channelNum <= IPWM_CH_MAX);
    for (i = 0U; i < config->channelNum; i++) {
        ASSERT_PARAM(&(config->combineChConfig[i]) != NULL);

        channel = config->combineChConfig[i].channel;
        IPWM_REG_SetOutputInitLevel(regBase, channel, config->combineChConfig[i].initLevel);
        IPWM_REG_SetOutputInit(regBase, channel, config->combineChConfig[i].initOutputEn);

        IPWM_REG_SetLocalPrescaler(regBase, channel, config->combineChConfig[i].counterConfig.localClkPsc);
        IPWM_REG_SetMod(regBase, channel, config->combineChConfig[i].counterConfig.modValue);
        IPWM_REG_SetCntInitVal(regBase, channel, config->combineChConfig[i].counterConfig.initValue);
        IPWM_REG_SetCVa(regBase, channel, config->combineChConfig[i].cva);
        IPWM_REG_SetCVb(regBase, channel, config->combineChConfig[i].cvb);

        IPWM_REG_SetChMode(regBase, channel, config->combineChConfig[i].mode);
        IPWM_REG_SetCntMode(regBase, channel, config->combineChConfig[i].countMode);
        IPWM_REG_SetEventaEdgeLevel(regBase, channel, (uint8_t)config->combineChConfig[i].eventaLevelMode);

        IPWM_REG_SetCVaMatchDir(regBase, channel, config->combineChConfig[i].cvaMatchDir);
        IPWM_REG_SetCVbMatchDir(regBase, channel, config->combineChConfig[i].cvbMatchDir);

        IPWM_REG_SetChPOL(regBase, channel, config->combineChConfig[i].chPolarity);

        IPWM_REG_SetMinTrigger(regBase, channel, config->combineChConfig[i].minTriggerEn);
        IPWM_REG_SetMaxTrigger(regBase, channel, config->combineChConfig[i].maxTriggerEn);
        IPWM_REG_SetCVaMatchTrigger(regBase, channel, config->combineChConfig[i].cvaMatchTriggerEn);
        IPWM_REG_SetCVbMatchTrigger(regBase, channel, config->combineChConfig[i].cvbMatchTriggerEn);

        IPWM_REG_SetOverflowIntEn(regBase, channel, config->combineChConfig[i].counterConfig.overflowIntEn);
        IPWM_REG_SetUnderflowIntEn(regBase, channel, config->combineChConfig[i].counterConfig.underflowIntEn);
        IPWM_REG_SetOverflowEventRatio(regBase, channel, config->combineChConfig[i].counterConfig.cntOverflowFreq);

        IPWM_REG_SetChEventaIntEn(regBase, channel, config->combineChConfig[i].eventaIntEn);
        IPWM_REG_SetChEventaRatio(regBase, channel, config->combineChConfig[i].eventaFreq);
        IPWM_REG_SetChEventbIntEn(regBase, channel, config->combineChConfig[i].eventbIntEn);
        IPWM_REG_SetChEventbRatio(regBase, channel, config->combineChConfig[i].eventbFreq);
        IPWM_REG_SetCounter(regBase, channel, 0U);
    }
#if (CONFIG_IPWM_UNIFIED_IRQ_FEATURE == 1U)
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_ipwmIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_ipwmIrqId[instance]);
    }
    s_ipwmCallback[instance] = config->callBack;
#else
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_ipwmChannelIrqId[instance]);
        NVIC_EnableIRQ(s_ipwmOverflowIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_ipwmChannelIrqId[instance]);
        NVIC_DisableIRQ(s_ipwmOverflowIrqId[instance]);
    }
    s_ipwmChannelCallback[instance] = config->channelCallBack;
    s_ipwmOverflowCallback[instance] = config->overflowCallBack;
#endif
    IPWM_REG_SetExternalTriggerRatio(regBase, config->triggerRatio);

    for (i = 0U; i < config->channelNum; i++) {
        channel = config->combineChConfig[i].channel;
        IPWM_REG_SetCntEn(regBase, channel, config->combineChConfig[i].counterConfig.cntEn);
    }
    IPWM_REG_SetGlobalPscVal(regBase, config->globalPscVal);
    IPWM_REG_SetGlobalPscEn(regBase, config->globalPscEn);
}

/*******************************************************************************
 *** Function Name : IPWM_InitDualEdgeCapture
 *** Description   : Configure the IPWM channel dual edge capture related functions
 ******************************************************************************/
void IPWM_InitDualEdgeCapture(uint32_t instance, const ipwm_dual_edge_capture_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    IPWM_Type* regBase = s_ipwmRegBase[instance];
    uint8_t i = 0U;
    ipwm_channel_t channel = IPWM_CH_0;

    /* Enable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_ipwmModuleName[instance], true);
    /* Set soft reset inactive of spwm module */
    CLOCK_SetModuleSoftResetState(s_ipwmModuleName[instance], RESET_INACTIVE);

    ASSERT_PARAM(config->channelNum <= IPWM_CH_MAX);
    for (i = 0U; i < config->channelNum; i++) {
        ASSERT_PARAM(&(config->channelConfig[i]) != NULL);

        channel = config->channelConfig[i].channel;
        IPWM_REG_SetLocalPrescaler(regBase, channel, config->channelConfig[i].counterConfig.localClkPsc);
        IPWM_REG_SetMod(regBase, channel, config->channelConfig[i].counterConfig.modValue);
        IPWM_REG_SetCntInitVal(regBase, channel, config->channelConfig[i].counterConfig.initValue);

        IPWM_REG_SetChMode(regBase, channel, config->channelConfig[i].mode);
        IPWM_REG_SetCntMode(regBase, channel, IPWM_UP_COUNT);
        switch (config->channelConfig[i].captureType) {
            case IPWM_POSITIVE_PLUSE_WIDTH_CAPTURE:
                IPWM_REG_SetEventaEdgeLevel(regBase, channel, (uint8_t)IPWM_RISING_EDGE);
                IPWM_REG_SetEventbEdgeLevel(regBase, channel, (uint8_t)IPWM_FALLING_EDGE);
                break;

            case IPWM_NEGATIVE_PLUSE_WIDTH_CAPTURE:
                IPWM_REG_SetEventaEdgeLevel(regBase, channel, (uint8_t)IPWM_FALLING_EDGE);
                IPWM_REG_SetEventbEdgeLevel(regBase, channel, (uint8_t)IPWM_RISING_EDGE);
                break;

            case IPWM_RISING_EDGE_PERIOD_CAPTURE:
                IPWM_REG_SetEventaEdgeLevel(regBase, channel, (uint8_t)IPWM_RISING_EDGE);
                IPWM_REG_SetEventbEdgeLevel(regBase, channel, (uint8_t)IPWM_RISING_EDGE);
                break;

            case IPWM_FALLING_EDGE_PERIOD_CAPTURE:
                IPWM_REG_SetEventaEdgeLevel(regBase, channel, (uint8_t)IPWM_FALLING_EDGE);
                IPWM_REG_SetEventbEdgeLevel(regBase, channel, (uint8_t)IPWM_FALLING_EDGE);
                break;

            default:
                break;
        }
        IPWM_REG_SetInputFilterVal(regBase, channel, config->channelConfig[i].chFilterValue);
        IPWM_REG_SetChEventaReset(regBase, channel, config->channelConfig[i].eventaRst);
        IPWM_REG_SetChEventbReset(regBase, channel, config->channelConfig[i].eventbRst);

        IPWM_REG_SetOverflowIntEn(regBase, channel, config->channelConfig[i].counterConfig.overflowIntEn);
        IPWM_REG_SetUnderflowIntEn(regBase, channel, config->channelConfig[i].counterConfig.underflowIntEn);
        IPWM_REG_SetOverflowEventRatio(regBase, channel, config->channelConfig[i].counterConfig.cntOverflowFreq);

        IPWM_REG_SetChEventaIntEn(regBase, channel, config->channelConfig[i].eventaIntEn);
        IPWM_REG_SetChEventaRatio(regBase, channel, config->channelConfig[i].eventaFreq);
        IPWM_REG_SetChEventbIntEn(regBase, channel, config->channelConfig[i].eventbIntEn);
        IPWM_REG_SetChEventbRatio(regBase, channel, config->channelConfig[i].eventbFreq);
        IPWM_REG_SetCounter(regBase, channel, 0U);
    }

    IPWM_REG_SetHallMode(regBase, config->hallEn);
    IPWM_REG_SetInputFilterPsc(regBase, config->filterPsc);

#if (CONFIG_IPWM_UNIFIED_IRQ_FEATURE == 1U)
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_ipwmIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_ipwmIrqId[instance]);
    }
    s_ipwmCallback[instance] = config->callBack;
#else
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_ipwmChannelIrqId[instance]);
        NVIC_EnableIRQ(s_ipwmOverflowIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_ipwmChannelIrqId[instance]);
        NVIC_DisableIRQ(s_ipwmOverflowIrqId[instance]);
    }
    s_ipwmChannelCallback[instance] = config->channelCallBack;
    s_ipwmOverflowCallback[instance] = config->overflowCallBack;
#endif

    for (i = 0; i < config->channelNum; i++) {
        channel = config->channelConfig[i].channel;
        IPWM_REG_SetCntEn(regBase, channel, config->channelConfig[i].counterConfig.cntEn);
    }
    IPWM_REG_SetGlobalPscVal(regBase, config->globalPscVal);
    IPWM_REG_SetGlobalPscEn(regBase, config->globalPscEn);
}

/*******************************************************************************
 *** Function Name : IPWM_SetChannelMode
 *** Description   : Configure the IPWM channel work mode
 ******************************************************************************/
void IPWM_SetChannelMode(uint32_t instance, ipwm_channel_t channel, ipwm_channel_mode_t mode)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetChMode(regBase, channel, mode);
}

/*******************************************************************************
 *** Function Name : IPWM_SetChannelSoftwareControl
 *** Description   : Configure the IPWM channel software ouput control related functions
 ******************************************************************************/
void IPWM_SetChannelSoftwareControl(uint32_t instance, ipwm_channel_t channel, bool enable,
                                    ipwm_output_level_t level)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetSoftwareControlLevel(regBase, channel, level);
    IPWM_REG_SetSoftwareControlEn(regBase, channel, enable);
}

/*******************************************************************************
 *** Function Name : IPWM_SetChannelOutputMask
 *** Description   : Configure the IPWM channel output mask related functions
 ******************************************************************************/
void IPWM_SetChannelOutputMask(uint32_t instance, ipwm_channel_t channel, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetOutputMask(regBase, channel, enable);
}

/*******************************************************************************
 *** Function Name : IPWM_SetModDither
 *** Description   : Configure the IPWM modulo dither value
 ******************************************************************************/
void IPWM_SetModDither(uint32_t instance, ipwm_channel_t channel, uint8_t modDitherValue)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetModDither(regBase, channel, modDitherValue);
}

/*******************************************************************************
 *** Function Name : IPWM_SetChDither
 *** Description   : Configure the IPWM channel dither value
 ******************************************************************************/
void IPWM_SetChDither(uint32_t instance, ipwm_channel_t channel, uint8_t cvaDitherValue, uint8_t cvbDitherValue)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetCVaDither(regBase, channel, cvaDitherValue);
    IPWM_REG_SetCVbDither(regBase, channel, cvbDitherValue);
}

/*******************************************************************************
 *** Function Name : IPWM_SetCntInitVal
 *** Description   : Configure the IPWM counter initial value
 ******************************************************************************/
void IPWM_SetCntInitVal(uint32_t instance, ipwm_channel_t channel, uint16_t cntInitValue)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetCntInitVal(regBase, channel, cntInitValue);
}

/*******************************************************************************
 *** Function Name : IPWM_SetChannelValue
 *** Description   : Configure the IPWM channel match value
 ******************************************************************************/
void IPWM_SetChannelValue(uint32_t instance, ipwm_channel_t channel, uint16_t cva, uint16_t cvb)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetCVa(regBase, channel, cva);
    IPWM_REG_SetCVb(regBase, channel, cvb);
}

/*******************************************************************************
 *** Function Name : IPWM_SetMod
 *** Description   : Configure the IPWM counter modulo value
 ******************************************************************************/
void IPWM_SetMod(uint32_t instance, ipwm_channel_t channel, uint16_t modValue)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetMod(regBase, channel, modValue);
}

/*******************************************************************************
 *** Function Name : IPWM_SetCounter
 *** Description   : Set the IPWM counter value, this function is used to make
 ***                 the counter restart from cntin, cntValue has no valid meaning
 ******************************************************************************/
void IPWM_SetCounter(uint32_t instance, ipwm_channel_t channel, uint16_t cntValue)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetCounter(regBase, channel, cntValue);
}

/*******************************************************************************
 *** Function Name : IPWM_GetCounter
 *** Description   : Get the IPWM current counter value
 ******************************************************************************/
uint16_t IPWM_GetCounter(uint32_t instance, ipwm_channel_t channel)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];
    uint16_t cntValue = 0U;

    cntValue = IPWM_REG_GetCounter(regBase, channel);

    return cntValue;
}

/*******************************************************************************
 *** Function Name : IPWM_GetCVa
 *** Description   : Get the IPWM channel value a
 ******************************************************************************/
uint16_t IPWM_GetCVa(uint32_t instance, ipwm_channel_t channel)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);

    IPWM_Type* regBase = s_ipwmRegBase[instance];
    uint16_t chVal = 0U;

    chVal = IPWM_REG_GetCVa(regBase, channel);

    return chVal;
}

/*******************************************************************************
 *** Function Name : IPWM_GetCVb
 *** Description   : Get the IPWM channel value b
 ******************************************************************************/
uint16_t IPWM_GetCVb(uint32_t instance, ipwm_channel_t channel)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);

    IPWM_Type* regBase = s_ipwmRegBase[instance];
    uint16_t chVal = 0U;

    chVal = IPWM_REG_GetCVb(regBase, channel);

    return chVal;
}

/*******************************************************************************
 *** Function Name : IPWM_GetChLevel
 *** Description   : Get the IPWM channel level
 ******************************************************************************/
uint8_t IPWM_GetChannelLevel(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);

    IPWM_Type* regBase = s_ipwmRegBase[instance];
    uint8_t level = 0U;

    level = IPWM_REG_GetChLevel(regBase);

    return level;
}

#if (CONFIG_IPWM_SUPPORT_DECODER_DIR == 1U)
/*******************************************************************************
 *** Function Name : IPWM_GetQuadDecoderDir
 *** Description   : Get the IPWM quad decoder counter direction
 ******************************************************************************/
bool IPWM_GetQuadDecoderDir(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);

    IPWM_Type* regBase = s_ipwmRegBase[instance];
    bool direction = IPWM_QUAD_DIR_DOWN;

    direction = IPWM_REG_GetQuadDecoderDir(regBase);

    return direction;
}
#endif

/*******************************************************************************
 *** Function Name : IPWM_InitQuadDecoderMode
 *** Description   : Configure the IPWM quad decoder related functions
 ******************************************************************************/
void IPWM_InitQuadDecoderMode(uint32_t instance, const ipwm_quad_decoder_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    /* Enable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_ipwmModuleName[instance], true);
    /* Set soft reset inactive of spwm module */
    CLOCK_SetModuleSoftResetState(s_ipwmModuleName[instance], RESET_INACTIVE);

    IPWM_REG_SetMod(regBase, IPWM_CH_0, config->counterConfig.modValue);
    IPWM_REG_SetCntInitVal(regBase, IPWM_CH_0, config->counterConfig.initValue);

    IPWM_REG_SetInputFilterVal(regBase, IPWM_CH_0, config->phaseAfilterValue);
    IPWM_REG_SetInputFilterVal(regBase, IPWM_CH_1, config->phaseBfilterValue);
    IPWM_REG_SetInputFilterVal(regBase, IPWM_CH_2, config->phaseZfilterValue);
    IPWM_REG_SetInputFilterPsc(regBase, config->filterPsc);

    IPWM_REG_SetPhaseAPolarity(regBase, config->phaseAPol);
    IPWM_REG_SetPhaseBPolarity(regBase, config->phaseBPol);
    IPWM_REG_SetPhaseZPolarity(regBase, config->phaseZPol);

    IPWM_REG_SetPhaseZReset(regBase, config->phaseZRstEn);

    IPWM_REG_SetOverflowIntEn(regBase, IPWM_CH_0, config->counterConfig.overflowIntEn);
    IPWM_REG_SetUnderflowIntEn(regBase, IPWM_CH_0, config->counterConfig.underflowIntEn);
    IPWM_REG_SetOverflowEventRatio(regBase, IPWM_CH_0, config->counterConfig.cntOverflowFreq);

#if (CONFIG_IPWM_SUPPORT_PHASEZ_INT == 1U)
    IPWM_REG_SetPhaseZIntEnable(regBase, config->phaseZIntEn);
#endif
    IPWM_REG_SetQuadDecoderMode(regBase, config->mode);
    IPWM_REG_SetQuadDecoder(regBase, config->quadEn);

    IPWM_REG_SetCounter(regBase, IPWM_CH_0, 0U);

#if (CONFIG_IPWM_UNIFIED_IRQ_FEATURE == 1U)
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_ipwmIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_ipwmIrqId[instance]);
    }
    s_ipwmCallback[instance] = config->callBack;
#else
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_ipwmChannelIrqId[instance]);
        NVIC_EnableIRQ(s_ipwmOverflowIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_ipwmChannelIrqId[instance]);
        NVIC_DisableIRQ(s_ipwmOverflowIrqId[instance]);
    }
    s_ipwmChannelCallback[instance] = config->channelCallBack;
    s_ipwmOverflowCallback[instance] = config->overflowCallBack;
#endif
    IPWM_REG_SetCntEn(regBase, IPWM_CH_0, config->counterConfig.cntEn);
}

/*******************************************************************************
 *** Function Name : IPWM_SetWriteProtect
 *** Description   : Configure the IPWM write protect function
 ******************************************************************************/
void IPWM_SetWriteProtect(uint32_t instance, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetWriteProtect(regBase, enable);
}

/*******************************************************************************
 *** Function Name : IPWM_InitSyncContol
 *** Description   : Configure the IPWM register sync related functions
 ******************************************************************************/
void IPWM_InitSyncContol(uint32_t instance, ipwm_sync_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);

    IPWM_Type* regBase = s_ipwmRegBase[instance];
    uint8_t i = 0U;
    ipwm_channel_t channel = IPWM_CH_0;

    ASSERT_PARAM(config->channelNum <= IPWM_CH_MAX);
    for (i = 0; i < config->channelNum; i++) {
        ASSERT_PARAM(&(config->syncConfig[i]) != NULL);

        channel = config->syncConfig[i].channel;
        IPWM_REG_SetModSoftwareSync(regBase, channel, config->syncConfig[i].counterSyncEn);
        IPWM_REG_SetCnvSync(regBase, channel, config->syncConfig[i].counterSyncEn);
        if ((config->syncConfig[i].maxLoadingPointEn == false) && (config->syncConfig[i].minLoadingPointEn == false)) {
            IPWM_REG_SetCounterSoftwareSync(regBase, channel, true);
        } else {
            IPWM_REG_SetCounterSoftwareSync(regBase, channel, false);
            IPWM_REG_SetMaxLoadPoint(regBase, channel, config->syncConfig[i].maxLoadingPointEn);
            IPWM_REG_SetMinLoadPoint(regBase, channel, config->syncConfig[i].minLoadingPointEn);
        }
        IPWM_REG_SetSyncEn(regBase, channel, config->syncConfig[i].chSyncEn);
    }
}

/*******************************************************************************
 *** Function Name : IPWM_SetSoftwareTriggerSync
 *** Description   : Generate a software trigger to sync register value
 ******************************************************************************/
void IPWM_SetSoftwareTriggerSync(uint32_t instance, ipwm_channel_t channel)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetSoftwareTriggerSync(regBase, channel, true);
}

/*******************************************************************************
 *** Function Name : IPWM_SetCntEn
 *** Description   : Configure the IPWM counter enable or disable
 ******************************************************************************/
void IPWM_SetCntEn(uint32_t instance, ipwm_channel_t channel, bool clkEn)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < IPWM_CH_MAX);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetCntEn(regBase, channel, clkEn);
}

/*******************************************************************************
 *** Function Name : IPWM_SetGlobalPscEn
 *** Description   : Configure the IPWM global prescaler enable or disable
 ******************************************************************************/
void IPWM_SetGlobalPscEn(uint32_t instance, bool clkEn)
{
    ASSERT_PARAM(CONFIG_IPWM_INSTANCE_COUNT > instance);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetGlobalPscEn(regBase, clkEn);
}

/*******************************************************************************
 *** Function Name : IPWM_SetGlobalTimeBaseEnable
 *** Description   : Configure the IPWM global time base enable function
 ******************************************************************************/
void IPWM_SetGlobalTimeBaseEnable(uint32_t instance, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetGlobalTimeBaseEnable(regBase, enable);
}

/*******************************************************************************
 *** Function Name : IPWM_SetGlobalTimeBaseOutput
 *** Description   : Configure the IPWM global time base output function
 ******************************************************************************/
void IPWM_SetGlobalTimeBaseOutput(uint32_t instance, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);

    IPWM_Type* regBase = s_ipwmRegBase[instance];

    IPWM_REG_SetGlobalTimeBaseOutput(regBase, enable);
}

/*******************************************************************************
 *** Function Name : IPWM_Deinit
 *** Description   : Deinit IPWM module
 ******************************************************************************/
void IPWM_Deinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_IPWM_INSTANCE_COUNT);

    /* Set soft reset active of spwm module */
    CLOCK_SetModuleSoftResetState(s_ipwmModuleName[instance], RESET_ACTIVE);
    /* Disable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_ipwmModuleName[instance], false);
#if (CONFIG_IPWM_UNIFIED_IRQ_FEATURE == 1U)
    NVIC_DisableIRQ(s_ipwmIrqId[instance]);
    NVIC_ClearPendingIRQ(s_ipwmIrqId[instance]);
    s_ipwmCallback[instance] = NULL;
#else
    NVIC_DisableIRQ(s_ipwmChannelIrqId[instance]);
    NVIC_ClearPendingIRQ(s_ipwmChannelIrqId[instance]);
    s_ipwmChannelCallback[instance] = NULL;
    NVIC_DisableIRQ(s_ipwmOverflowIrqId[instance]);
    NVIC_ClearPendingIRQ(s_ipwmOverflowIrqId[instance]);
    s_ipwmOverflowCallback[instance] = NULL;
#endif
}

#if (CONFIG_IPWM_UNIFIED_IRQ_FEATURE == 1U)
/*******************************************************************************
 *** Function Name : IPWM_IRQHandlerProcess
 *** Description   : IPWM interrupt implement
 ******************************************************************************/
void IPWM_IRQHandlerProcess(uint32_t instance)
{
    IPWM_Type* regBase = s_ipwmRegBase[instance];
    ipwm_int_state_t* intStatus;

    intStatus = &s_ipwmIntStatus[instance];

    intStatus->overflowStatus = IPWM_REG_GetAllChOverflowFlag(regBase);
    intStatus->underflowStatus = IPWM_REG_GetAllChUnderflowFlag(regBase);
    intStatus->channelStatus = IPWM_REG_GetChStatus(regBase);

    if (intStatus->overflowStatus) {
        IPWM_REG_ClrAllChOverflowFlag(regBase, intStatus->overflowStatus);
    }

    if (intStatus->underflowStatus) {
        IPWM_REG_ClrAllChUnderflowFlag(regBase, intStatus->underflowStatus);
    }

    if (intStatus->channelStatus) {
        IPWM_REG_ClrChStatus(regBase, intStatus->channelStatus);
    }

    if ((s_ipwmCallback[instance] != NULL) && ((intStatus->overflowStatus != 0U) || (intStatus->underflowStatus != 0U) || (intStatus->channelStatus != 0U))) {
        s_ipwmCallback[instance](instance, (void*)intStatus);
    }
}
#else
/*******************************************************************************
 *** Function Name : IPWM_Channel_IRQHandlerProcess
 *** Description   : IPWM channel interrupt interrupt implement
 ******************************************************************************/
void IPWM_Channel_IRQHandlerProcess(uint32_t instance)
{
    IPWM_Type* regBase = s_ipwmRegBase[instance];
    ipwm_int_state_t* intStatus = &s_ipwmIntStatus[instance];

    intStatus->channelStatus = IPWM_REG_GetChStatus(regBase);
    if (intStatus->channelStatus) {
        IPWM_REG_ClrChStatus(regBase, intStatus->channelStatus);
    }

    if ((s_ipwmChannelCallback[instance] != NULL) && (intStatus->channelStatus != 0U)) {
        s_ipwmChannelCallback[instance](instance, (void*)intStatus);
    }
}

/*******************************************************************************
 *** Function Name : IPWM_Overflow_IRQHandlerProcess
 *** Description   : IPWM overflow interrupt interrupt implement
 ******************************************************************************/
void IPWM_Overflow_IRQHandlerProcess(uint32_t instance)
{
    IPWM_Type* regBase = s_ipwmRegBase[instance];
    ipwm_int_state_t* intStatus = &s_ipwmIntStatus[instance];

    intStatus->overflowStatus = IPWM_REG_GetAllChOverflowFlag(regBase);
    intStatus->underflowStatus = IPWM_REG_GetAllChUnderflowFlag(regBase);
    intStatus->phaseZStatus = IPWM_REG_GetPhaseZStatus(regBase);

    if (intStatus->overflowStatus) {
        IPWM_REG_ClrAllChOverflowFlag(regBase, intStatus->overflowStatus);
    }

    if (intStatus->underflowStatus) {
        IPWM_REG_ClrAllChUnderflowFlag(regBase, intStatus->underflowStatus);
    }

    if (intStatus->phaseZStatus) {
        IPWM_REG_ClrPhaseZStatus(regBase);
    }

    if ((s_ipwmOverflowCallback[instance] != NULL) && ((intStatus->overflowStatus != 0U) || (intStatus->underflowStatus != 0U) || (intStatus->phaseZStatus != 0U))) {
        s_ipwmOverflowCallback[instance](instance, (void*)intStatus);
    }
}
#endif
/*******EOF********************************************************************/

